package TrieTree;

/*
添加与搜索单词 - 数据结构设计
请你设计一个数据结构，支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。

实现词典类 WordDictionary ：

WordDictionary() 初始化词典对象
void addWord(word) 将 word 添加到数据结构中，之后可以对它进行匹配
bool search(word) 如果数据结构中存在字符串与word 匹配，则返回 true ；否则，返回 false 。word 中可能包含一些 '.' ，每个. 都可以表示任何一个字母。

示例：
输入：
["WordDictionary","addWord","addWord","addWord","search","search","search","search"]
[[],["bad"],["dad"],["mad"],["pad"],["bad"],[".ad"],["b.."]]
输出：
[null,null,null,null,false,true,true,true]
解释：
WordDictionary wordDictionary = new WordDictionary();
wordDictionary.addWord("bad");
wordDictionary.addWord("dad");
wordDictionary.addWord("mad");
wordDictionary.search("pad"); // 返回 False
wordDictionary.search("bad"); // 返回 True
wordDictionary.search(".ad"); // 返回 True
wordDictionary.search("b.."); // 返回 True

作者：LeetCode
链接：https://leetcode.cn/leetbook/read/trie/x0jtri/
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

public class _33添加与搜索单词_数据结构设计 {
    public static void main(String[] args) {
        WordDictionary trie = new WordDictionary();
        trie.insert("a");
        trie.insert("a");
        System.out.println(trie.search("a."));

    }

    //map字典树 + 递归
    static class WordDictionary {

        static class TrieNode{
            HashMap<Character, TrieNode> children = new HashMap<>();
            boolean isEnd = false;
        }

        private static TrieNode root;

        public static void insert(String word) {
            TrieNode cur = root;
            for (int i = 0; i < word.length(); i++) {
                char ch = word.charAt(i);
                if(cur.children.get(ch) == null){
                    cur.children.put(ch, new TrieNode());
                }
                cur = cur.children.get(ch);
            }
            cur.isEnd = true;
        }

        public WordDictionary() {
            root = new TrieNode();
        }

        public void addWord(String word) {
            insert(word);
        }

        public static boolean search(String word) {
            return dfs(word,root);
        }

        public static boolean dfs(String word, TrieNode root) {
            for (int i = 0; i < word.length(); i++) {
                char ch = word.charAt(i);
                if(ch == '.'){
                    Set<Character> characters = root.children.keySet();
                    for (Character character : characters) {
                        if (dfs(word.substring(i+1), root.children.get(character))) {
                            return true;
                        }
                    }
                    return false;
                }else {
                    if (root.children.get(ch) == null) {
                        return false;
                    }
                    root = root.children.get(ch);
                }
            }
            return root.isEnd;
        }
    }



    //官解：数组字典树 + 递归
    class WordDictionary2 {
        private Trie root;

        public WordDictionary2() {
            root = new Trie();
        }

        public void addWord(String word) {
            root.insert(word);
        }

        public boolean search(String word) {
            return dfs(word, 0, root);
        }

        private boolean dfs(String word, int index, Trie node) {
            if (index == word.length()) {
                return node.isEnd();
            }
            char ch = word.charAt(index);
            if (Character.isLetter(ch)) {
                int childIndex = ch - 'a';
                Trie child = node.getChildren()[childIndex];
                if (child != null && dfs(word, index + 1, child)) {
                    return true;
                }
            } else {
                for (int i = 0; i < 26; i++) {
                    Trie child = node.getChildren()[i];
                    if (child != null && dfs(word, index + 1, child)) {
                        return true;
                    }
                }
            }
            return false;
        }
    }

    class Trie {
        private Trie[] children;
        private boolean isEnd;

        public Trie() {
            children = new Trie[26];
            isEnd = false;
        }

        public void insert(String word) {
            Trie node = this;
            for (int i = 0; i < word.length(); i++) {
                char ch = word.charAt(i);
                int index = ch - 'a';
                if (node.children[index] == null) {
                    node.children[index] = new Trie();
                }
                node = node.children[index];
            }
            node.isEnd = true;
        }

        public Trie[] getChildren() {
            return children;
        }

        public boolean isEnd() {
            return isEnd;
        }
    }

}
